package com.moli.lumpSugar.gateway.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.moli.lumpSugar.common.constant.RedisKey;
import com.moli.lumpSugar.common.exception.HttpErrorEnum;
import com.moli.lumpSugar.common.utils.JwtUtils;
import com.moli.lumpSugar.common.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @program: LumpSugarChatMicroservice
 * @description: Token验证过滤器
 * @author: zhangjt
 * @create: 2024-11-27 15:18
 **/
@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {
    public static final String HEADER_AUTHORIZATION = "Authorization";
    public static final String AUTHORIZATION_SCAN = "Bearer ";
    public static final String UID = "uid";
    public static final int TOKEN_EXPIRE_DAY = 3;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private ThreadPoolTaskExecutor lumpSugarChatExecutor;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 从请求头获取token
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String token = getToken(request);
        Long validUid = getValidUid(token);
        if (Objects.isNull(validUid)) {
            // 判断请求路径是否为公开路径
            String[] split = request.getURI().getPath().split("/");
            if (split.length > 2 && "public".equals(split[3]) || "netty".equals(split[1])) {
                return chain.filter(exchange);
            }
            response.setRawStatusCode(HttpErrorEnum.ACCESS_DENIED.getErrorCode());
            return response.setComplete();
        }
        renewalTokenIfNecessary(token);
        ServerWebExchange buildExchange = exchange.mutate().request(req -> req.header(UID, String.valueOf(validUid))).build();
        return chain.filter(buildExchange);
    }

    private void renewalTokenIfNecessary(String token) {
        lumpSugarChatExecutor.execute(()->{
            Long validUid = getValidUid(token);
            if (Objects.isNull(validUid)) {
                return;
            }
            Long expire = RedisUtils.getExpire(getUserTokenKey(validUid), TimeUnit.DAYS);
            if (expire == -2) {
                return;
            }
            if (expire < 1) {
                RedisUtils.expire(getUserTokenKey(validUid), TOKEN_EXPIRE_DAY, TimeUnit.DAYS);
            }
        });
    }

    private String getToken(ServerHttpRequest request) {
        String token = null;
        List<String> tokenList = request.getHeaders().get(HEADER_AUTHORIZATION);
        if (CollectionUtil.isNotEmpty(tokenList)) {
            token = tokenList.get(0);
        }
        return Optional.ofNullable(token)
                .filter(h -> h.startsWith(AUTHORIZATION_SCAN))
                .map(h -> h.replaceFirst(AUTHORIZATION_SCAN, ""))
                .orElse(null);
    }

    private Long getValidUid(String token) {
        Long uidOrNull = jwtUtils.getUidOrNull(token);
        if (Objects.isNull(uidOrNull)) {
            return null;
        }
        String oldToken = RedisUtils.getStr(getUserTokenKey(uidOrNull));
        return Objects.equals(oldToken, token) ? uidOrNull : null;
    }

    private String getUserTokenKey(Long uid) {
        return RedisKey.getKey(RedisKey.USER_TOKEN_STRING, uid);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
